package com.shengwei.truck.driver.socket;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.google.gson.Gson;
import com.shengwei.truck.driver.view.ui.app.MyApplication;
import com.shengwei.truck.driver.bean.LocationPack;
import com.shengwei.truck.driver.bean.MyConstant;
import com.shengwei.truck.driver.bean.User;
import com.shengwei.truck.driver.utils.AlarmManagerUtils;
import com.shengwei.truck.driver.utils.CommonUtil;
import com.shengwei.truck.driver.utils.MyLogCat;
import com.shengwei.truck.driver.utils.MyLogFile;
import com.shengwei.truck.driver.utils.SPUtils;

/**
 * Socket管理类, 负责socket连接，断开，发送心跳，发送位置
 */
public class SocketManager {
    private String TAG = "SocketManagerTcpClient";
    private static Context mContext;
    private static SocketManager instance = null;
    // 服务器连接相关，IP地址等
    private String ip = MyConstant.SOCKET_IP;
    private int port = MyConstant.SOCKET_IP_PORT;
    protected final static int reConnInterval = 60; //重连间隔
    private boolean mIsConnOk = false;  //防止重复连接
    private int mConnTimes = 1;  // 第几次连接
    // MINA Socket框架的Tcp客户端对象
    private TcpClient mTcpClient = null;
    //线程：负责连接服务器
    private Thread myConnectServerThread;
    private ConnServerRunnable mConnServerRunnable;

    /**
     * 单例，保证和服务器只有一个TCP连接
     * @param context
     * @return
     */
    public synchronized static SocketManager getInstance(Context context) {
        if (instance == null) {
            instance = new SocketManager();
            mContext = context;
        }
        return instance;
    }

    /**
     * 是否已连接
     * @return
     */
    public boolean IsConnOk() {
        return mIsConnOk;
    }

    /**
     * 设置是否已连接标识
     * @param isConnOk
     */
    public void setConnServer(boolean isConnOk) {
        mIsConnOk = isConnOk;
    }

    /**
     * 连接服务器，需要加锁，以防在多个线程里重复连接，即使SocketManager是单例。
     */
    public synchronized void connectServer() {
        MyLogFile.i(TAG,"connectServer threadId="+Thread.currentThread().getId());
        if(mIsConnOk) {
            MyLogFile.i(TAG,"connectServer tcp client has connected");
            return;
        }
        // 连接前先释放一些socket对象
        clear(); //建议把上述这些代码放到mConnServerRunnable里，现在先不移动了害怕出问题，这个又需要长期测试。
        TcpClient.mIsReConn = true;
        // 这里启动一个子线程是因为 网络连接是耗时操作容易引起ANR
        if(mConnServerRunnable == null) {
            MyLogCat.i(TAG,"new mConnServerRunnable");
            mConnServerRunnable = new ConnServerRunnable();
        }
        if(myConnectServerThread == null){
            MyLogCat.i(TAG,"new Thread with  mConnServerRunnable");
            myConnectServerThread = new Thread(mConnServerRunnable);
        }
        if(!(myConnectServerThread.isAlive())) {
            MyLogCat.i(TAG,"isAlive()="+myConnectServerThread.isAlive());
        }
        myConnectServerThread.start();
    }

    /**
     * “连接服务器线程 ”执行的 具体工作在Runnable里
     */
    public class ConnServerRunnable implements Runnable {
        @Override
        public void run() {
            synchronized (ConnServerRunnable.class) {
                if(mIsConnOk) {
                    return;
                }
                // 开始连接TCP服务器
                startConnect();
            }
        }

        /**
         * 开始连接TCP服务器
         */
        public void startConnect() {
            while (true) {
                try {
                    // 判断网络是否可用
                    MyLogFile.i(TAG, "TCP准备初次连接...第" + mConnTimes + "次");
                    boolean isNetAvailable = CommonUtil.isNetworkAvailable(mContext);
                    // 网络不可用时，间隔1分钟后，继续检查网络
                    if (!isNetAvailable) {
                        sleepInterval();
                        MyLogCat.i(TAG, "第" + mConnTimes + "次连接时，网络不可用");
                        mConnTimes++;
                        continue;
                    }
                    // 网络可用时，new TcpClient让socket client去连接服务器
                    if (mTcpClient == null) {
                        MyLogFile.i(TAG, "第" + mConnTimes + "次连接时，网络OK,准备连接服务器 in new TcpClient");
                        mTcpClient = new TcpClient(ip, port, mContext);  //在这里就已经连接到服务器了
                    } else {
                        MyLogFile.i(TAG, "第" + mConnTimes + "次连接时，网络OK,准备连接服务器 invoke getConnectorInstancet");
                        mTcpClient.getConnectorInstance(ip, port, mContext);
                    }
                    //服务器连接成功，则return推出循环重连
                    if (mTcpClient.mSession != null && mTcpClient.mSession.isConnected()) {
                        //发送广播消息到 MainActivity 来提示  连接成功
                        Intent msgIntent = new Intent(MyConstant.SERVER_CONNECT_OK_BROADCAST); //将位置传递到 地图UI的 广播监听器
                        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(mContext);
                        localBroadcastManager.sendBroadcast(msgIntent);
                        mIsConnOk = true;
                        MyLogCat.i(TAG, "TCP初次连接OK...threadId="+Thread.currentThread().getId());
                        mConnTimes = 1;
                        return;
                    }
                    else{ //服务器连接失败，则隔上1分钟再重连
                        sleepInterval();
                    }
                    //服务器连接失败，则继续下一次连接，继续while循环直到连接成功
                    mConnTimes++;
                }
                catch (Exception e) {
                    try {
                        MyLogFile.i(TAG, "SocketService exception msg=" + e.getMessage() + " in connectServer()");
                        Thread.sleep(reConnInterval * 1000);
                    }
                    catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 休眠1分钟
     */
    public void sleepInterval() {
        try {
            Thread.sleep(reConnInterval * 1000);
        }
        catch (Exception e) {
            MyLogFile.e(TAG,"sleep exception msg="+e.getMessage());
            return;
        }
    }

    // 关闭连接，默认是在主线程里关闭
    public void clear() {
        if(myConnectServerThread != null) {
            MyLogCat.i(TAG,"closeThread threadId="+Thread.currentThread().getId());
            myConnectServerThread.interrupt();
            myConnectServerThread = null;
            mConnServerRunnable = null;
        }
        try {
            if (mTcpClient != null) {
                mTcpClient.mIsReConn = false;
                mTcpClient.mConnector.getFilterChain().remove("reconnection");
                mTcpClient.mConnector.dispose();
                mTcpClient.mConnector = null;
                mTcpClient.mSession = null;
                mTcpClient = null;
                mIsConnOk = false; //gxw+
            }
            Thread.sleep(20);
        }
        catch (InterruptedException e) {
            MyLogFile.e(TAG,"InterruptedException in clear()");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 发送心跳包
     */
    public  void sendHeart() {
        try {
            Log.i(TAG,"sendHeart threadId="+Thread.currentThread().getId());
            boolean isNetAvailable = CommonUtil.isNetworkAvailable(mContext);

            //网络不可用，发送失败
            if(!isNetAvailable) {
                MyLogFile.e(TAG, " send heart failed because NETWORK InAvailable ");
                return;
            }
            //网络可用，发送心跳数据
            if (mTcpClient != null && mTcpClient.mSession.isConnected()) {
                String name = "hello I am gaoxiaowei";
                byte[] nameBytes = name.getBytes();
               // mTcpClient.mSession.write(nameBytes);
            }
            else { //网络可用，但是TCP连接已断开，则重连
                mIsConnOk = false;
                MyLogFile.e(TAG, " send heart failed because tcpClient == null or tcpClient has disconnected! tcpClient="+mTcpClient );
                connectServer();
            }
        } catch (Exception e) {
            MyLogCat.e(TAG, "send heart exception: " +e.getMessage()+"SocketService");
            e.printStackTrace();
        }
        return;
    }

    /**
     * 发送定位报文
     * @param lat 纬度
     * @param lng 经度
     * @param locationTime 定位时间
     * @return 0表示成功发送定位报文，其余代表具体的错误原因见以下switch语句
     */
    public int sendLocationPack(double lat, double lng,long locationTime) {
        int sendResult = 0;
        String curTaskId = (!TextUtils.isEmpty(MyApplication.mCurTaskId)) ? MyApplication.mCurTaskId : SPUtils.getString(mContext,"curTaskId");
        MyLogCat.i(TAG,"curTaskId="+curTaskId);
        if(TextUtils.isEmpty(curTaskId)){
            MyLogFile.i(TAG, "失败发送定位报文,因为任务ID为空");
            return 5;
        }
        int  ret = writeLocation(curTaskId,lat,lng,locationTime);  //正常
        switch (ret) {
            case 0:
                sendResult = 0;
               // MyLogFile.i(TAG,"成功发送定位报文");
                break;
            case 2:
                sendResult = 2;
                MyLogFile.i(TAG, "失败发送定位报文,因为网络连接问题，具体原因：请检测手机网络设置");
                break;
            case 3:
                sendResult = 3;
                MyLogFile.i(TAG, "失败发送定位报文,因为TCP连接断开：正准备重连服务器,请大约1分钟后尝试");
                break;
            case 4:
                sendResult = 4;
                MyLogFile.i(TAG, "失败发送定位报文,程序内部异常");
                break;
            }
        return sendResult;
    }

    /**
     * socket发送位置数据
     * @param curTaskId
     * @param lat：纬度
     * @param lng：经度
     * @param locationTime：定位时间
     * @return 0
     */
    public int writeLocation(String curTaskId,double lat, double lng, long locationTime) {
        int errCode = 0;
        try {
            if (mTcpClient != null && mTcpClient.mSession.isConnected()) {
                LocationPack pack = new LocationPack();
                //请求除登录以外的接口，都要带上token
                String token = User.getToken();
                String tokenHead = User.getTokenHead();
                if(!TextUtils.isEmpty(token) && !TextUtils.isEmpty(tokenHead)) {
                    pack.setAuth(tokenHead+" "+token);
                    pack.setTaskId(curTaskId);
                    pack.setLat(String.valueOf(lat));
                    pack.setLng(String.valueOf(lng));
                    pack.setUpTime(locationTime);
                }
                String locationJson = new Gson().toJson(pack,LocationPack.class);
                locationJson += "$_";
                byte[] locBytes = locationJson.getBytes();
                mTcpClient.mSession.write(locBytes);
            }
            else {
                boolean isNetAvailable = CommonUtil.isNetworkAvailable(mContext);
                if(!isNetAvailable) {
                    errCode = 2;
                }
                else {
                    errCode = 3;
                }
            }
        } catch (Exception e) {
            errCode = 4;
            e.printStackTrace();
        }
        return errCode;
    }

    /**
     * 启动定时器，负责发送socket心跳包
     */
    public synchronized void startHeartTask() {
        AlarmManagerUtils alarmManagerUtils = AlarmManagerUtils.getInstance(mContext);
        alarmManagerUtils.createGetUpAlarmManager();
        alarmManagerUtils.getUpAlarmManagerStartWork();
    }
}
